package org.zjvis.datascience.common.interceptor;

import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.zjvis.datascience.common.constant.Constant;
import org.zjvis.datascience.common.dto.BaseDTO;
import org.zjvis.datascience.common.util.JwtUtil;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Map;
import java.util.Properties;

/**
 * @description Mybatis自定义解释器
 * @date 2021-08-05
 */
@Component
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MybatisInterceptor implements Interceptor {

    private final static Logger logger = LoggerFactory.getLogger("MybatisInterceptor");

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 获取 SQL 命令
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        // 获取参数
        Object parameter = invocation.getArgs()[1];
        if (parameter != null &&
                (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType))) {
            if (isBaseDTOChild(parameter)) {
                fill(parameter, sqlCommandType);
            } else if (isBatchInsert(parameter)) {
                batchFill(parameter, sqlCommandType);
            }
        }
        return invocation.proceed();
    }

    private boolean isBaseDTOChild(Object parameter) {
        return BaseDTO.class.isAssignableFrom(parameter.getClass());
    }

    private boolean isBatchInsert(Object parameter) {
        return parameter instanceof MapperMethod.ParamMap;
    }

    private void batchFill(Object parameter, SqlCommandType sqlCommandType) throws NoSuchFieldException {
        for (Object i : ((MapperMethod.ParamMap) parameter).entrySet()) {
            Object obj;
            if (!((obj = ((Map.Entry) i).getValue()) instanceof Collection)) {
                return;
            }
            Collection dtos = (Collection) obj;
            if (CollectionUtils.isEmpty(dtos)) {
                return;
            }
            for (Object dto : dtos) {
                if (!isBaseDTOChild(dto)) {
                    continue;
                }
                fill(dto, sqlCommandType);
            }
        }
    }

    private void fill(Object parameter, SqlCommandType sqlCommandType) throws NoSuchFieldException {
        Field field = parameter.getClass().getSuperclass().getDeclaredField(Constant.MODIFIER_FIELD);
        fillField(field, parameter);

        if (SqlCommandType.INSERT.equals(sqlCommandType)) {
            field = parameter.getClass().getSuperclass().getDeclaredField(Constant.CREATOR_FIELD);
            fillField(field, parameter);
        }
    }

    private void fillField(Field field, Object parameter) {
        field.setAccessible(true);
        try {
            if (field.get(parameter) == null) {
                field.set(parameter, JwtUtil.getCurrentUserId());
            }
        } catch (IllegalAccessException e) {
            logger.error(String.format("fill field error: %s", e.getMessage()), e);
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}
